home *** CD-ROM | disk | FTP | other *** search
/ Games of Daze / Infomagic - Games of Daze (Summer 1995) (Disc 1 of 2).iso / x2ftp / msdos / utils / trace27 / doprnt.c < prev    next >
C/C++ Source or Header  |  1994-10-01  |  21KB  |  851 lines

  1. /*
  2.  * Copyright (c) 1988 Regents of the University of California.
  3.  * All rights reserved.
  4.  *
  5.  * Redistribution and use in source and binary forms are permitted
  6.  * provided that the above copyright notice and this paragraph are
  7.  * duplicated in all such forms and that any documentation,
  8.  * advertising materials, and other materials related to such
  9.  * distribution and use acknowledge that the software was developed
  10.  * by the University of California, Berkeley.  The name of the
  11.  * University may not be used to endorse or promote products derived
  12.  * from this software without specific prior written permission.
  13.  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
  14.  * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
  15.  * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
  16.  */
  17.  
  18. /*
  19.  * Tailoring for Intel architectures, MS-DOS, embedded applications and
  20.  * costum memory models.
  21.  * Bug fixes (Added UARG).
  22.  * Diomidis Spinellis <dspin@leon.nrcps.ariadne-t.gr>.
  23.  */
  24.  
  25. /* 
  26.  * Define FLOAT if you want floating point to be handled.
  27.  * Leave it out for implementations without floating point (e.g. drivers, TSR).
  28.  */
  29. /*#define FLOAT /**/
  30.  
  31. /* 
  32.  * Define USE_STDIO if doprnt will be used for an stdio implementation.
  33.  * If USE_STDIO is not defined only sprintf and vsprintf will work.
  34.  */
  35. /*#define USE_STDIO /**/
  36.  
  37. /* Define INTEL if near/far pointer flags (NF) are to be supported */
  38. #define INTEL /**/
  39.  
  40. /* 
  41.  * Define DSNESS if the program under Intel processors has DS!=SS,
  42.  * e.g. has been compiled with MSC Aw or Au options.
  43.  */
  44. #define DSNESS /**/
  45.  
  46. /*
  47.  * Define FNPROTO if function prototypes are given in the include files.
  48.  */
  49. #define FNPROTO /**/
  50.  
  51. #ifndef unix
  52. typedef unsigned char u_char;
  53. typedef unsigned long u_long;
  54. #endif
  55.  
  56.  
  57. #if defined(LIBC_SCCS) && !defined(lint)
  58. static char sccsid[] = "@(#)doprnt.c    5.35 (Berkeley) 6/27/88";
  59. #endif /* LIBC_SCCS and not lint */
  60.  
  61. #include <sys/types.h>
  62. #include <varargs.h>
  63. #include <stdio.h>
  64. #include <ctype.h>
  65. #include <string.h>
  66. #ifdef INTEL
  67. #include <memory.h>    /* _fmemchr() */
  68. #endif
  69.  
  70. #ifdef DSNESS
  71. #undef va_arg
  72. #define va_arg(ap,t) ((t _far *)(ap += sizeof(t)))[-1]
  73. #endif
  74.  
  75. /* 11-bit exponent (VAX G floating point) is 308 decimal digits */
  76. #define    MAXEXP        308
  77. /* 128 bit fraction takes up 39 decimal digits; max reasonable precision */
  78. #define    MAXFRACT    39
  79.  
  80. #define    DEFPREC        6
  81.  
  82. #define    BUF        (MAXEXP+MAXFRACT+1)    /* + decimal point */
  83.  
  84. #ifdef USE_STDIO
  85. #define    PUTC(ch)    (void) putc(ch, fp)
  86. #else
  87. #define PUTC(_c) (--(fp)->_cnt, (0xff & (*(fp)->_ptr++ = (char)(_c))))
  88. #endif
  89.  
  90. #define    ARG() \
  91.     _ulong = flags&LONGINT ? va_arg(argp, long) : \
  92.         flags&SHORTINT ? va_arg(argp, short) : va_arg(argp, int);
  93.  
  94. #define    UARG() \
  95.     _ulong = flags&LONGINT ? va_arg(argp, unsigned long) : \
  96.         flags&SHORTINT ? va_arg(argp, unsigned short) : va_arg(argp, unsigned int);
  97.  
  98. #define    todigit(c)    ((c) - '0')
  99. #define    tochar(n)    ((n) + '0')
  100.  
  101. /* have to deal with the negative buffer count kludge */
  102. #ifdef unix
  103. #define    NEGATIVE_COUNT_KLUDGE
  104. #endif
  105.  
  106. #define    LONGINT        0x01        /* long integer */
  107. #define    LONGDBL        0x02        /* long double; unimplemented */
  108. #define    SHORTINT    0x04        /* short integer */
  109. #define    ALT        0x08        /* alternate form */
  110. #define    LADJUST        0x10        /* left adjustment */
  111. #define    ZEROPAD        0x20        /* zero (as opposed to blank) pad */
  112. #define    HEXPREFIX    0x40        /* add 0x or 0X prefix */
  113. #ifdef INTEL
  114. #define FARPTR        0x80        /* Far pointer */
  115. #define NEARPTR        0x100        /* Near pointer */
  116. #endif
  117.  
  118. #ifdef DSNESS
  119.  
  120. _doprnt(fmt0, ssargp, fp)
  121.     u_char *fmt0;
  122.     char _based(_segname("_STACK")) *ssargp;
  123. #else
  124. _doprnt(fmt0, argp, fp)
  125.     u_char *fmt0;
  126.     va_list argp;
  127. #endif
  128.     register FILE *fp;
  129. {
  130. #ifdef DSNESS
  131.     /*
  132.      * Microsoft C 6.00 is buggy and does not generate SS prefixes for
  133.      * access using STACK based pointers when compiled with DS!=SS
  134.      * So we have to use a far pointer to make it work.
  135.      */
  136.     char _far *argp = ssargp; /* Argument pointer */
  137. #endif
  138.     register u_char *fmt;    /* format string */
  139.     register int ch;    /* character from fmt */
  140.     register int cnt;    /* return value accumulator */
  141.     register int n;        /* random handy integer */
  142.     register char *t;    /* buffer pointer */
  143. #ifdef FLOAT
  144.     double _double;        /* double precision arguments %[eEfgG] */
  145. #endif
  146.     u_long _ulong;        /* integer arguments %[diouxX] */
  147.     int base;        /* base for [diouxX] conversion */
  148.     int dprec;        /* decimal precision in [diouxX] */
  149.     int fieldsz;        /* field size expanded by sign, etc */
  150.     int flags;        /* flags as above */
  151.     int fpprec;        /* `extra' floating precision in [eEfgG] */
  152.     int prec;        /* precision from format (%.3d), or -1 */
  153.     int realsz;        /* field size expanded by decimal precision */
  154.     int size;        /* size of converted field or string */
  155.     int width;        /* width from format (%8d), or 0 */
  156.     char sign;        /* sign prefix (' ', '+', '-', or \0) */
  157.     char softsign;        /* temporary negative sign for floats */
  158.     char *digs;        /* digits for [diouxX] conversion */
  159.     static char buf[BUF];    /* space for %c, %[diouxX], %[eEfgG] */
  160.  
  161.     if (fp->_flag & _IORW) {
  162.         fp->_flag |= _IOWRT;
  163.         fp->_flag &= ~(_IOEOF|_IOREAD);
  164.     }
  165.     if ((fp->_flag & _IOWRT) == 0)
  166.         return (EOF);
  167.  
  168.     fmt = fmt0;
  169.     digs = "0123456789abcdef";
  170.     for (cnt = 0;; ++fmt) {
  171.         n = fp->_cnt;
  172.         for (t = (char *)fp->_ptr; (ch = *fmt) && ch != '%';
  173.              ++cnt, ++fmt)
  174.             if (--n < 0
  175. #ifdef NEGATIVE_COUNT_KLUDGE
  176.                 && (!(fp->_flag & _IOLBF) || -n >= fp->_bufsiz)
  177. #endif
  178.                 || ch == '\n' && fp->_flag & _IOLBF) {
  179.                 fp->_cnt = n;
  180.                 fp->_ptr = t;
  181. #ifdef USE_STDIO
  182.                 (void) _flsbuf((u_char)ch, fp);
  183. #endif
  184.                 n = fp->_cnt;
  185.                 t = (char *)fp->_ptr;
  186.             } else
  187.                 *t++ = ch;
  188.         fp->_cnt = n;
  189.         fp->_ptr = t;
  190.         if (!ch)
  191.             return (cnt);
  192.  
  193.         flags = 0; dprec = 0; fpprec = 0; width = 0;
  194.         prec = -1;
  195.         sign = '\0';
  196.  
  197. rflag:        switch (*++fmt) {
  198.         case ' ':
  199.             /*
  200.              * ``If the space and + flags both appear, the space
  201.              * flag will be ignored.''
  202.              *    -- ANSI X3J11
  203.              */
  204.             if (!sign)
  205.                 sign = ' ';
  206.             goto rflag;
  207.         case '#':
  208.             flags |= ALT;
  209.             goto rflag;
  210.         case '*':
  211.             /*
  212.              * ``A negative field width argument is taken as a
  213.              * - flag followed by a  positive field width.''
  214.              *    -- ANSI X3J11
  215.              * They don't exclude field widths read from args.
  216.              */
  217.             if ((width = va_arg(argp, int)) >= 0)
  218.                 goto rflag;
  219.             width = -width;
  220.             /* FALLTHROUGH */
  221.         case '-':
  222.             flags |= LADJUST;
  223.             goto rflag;
  224.         case '+':
  225.             sign = '+';
  226.             goto rflag;
  227.         case '.':
  228.             if (*++fmt == '*')
  229.                 n = va_arg(argp, int);
  230.             else {
  231.                 n = 0;
  232.                 while (isascii(*fmt) && isdigit(*fmt))
  233.                     n = 10 * n + todigit(*fmt++);
  234.                 --fmt;
  235.             }
  236.             prec = n < 0 ? -1 : n;
  237.             goto rflag;
  238.         case '0':
  239.             /*
  240.              * ``Note that 0 is taken as a flag, not as the
  241.              * beginning of a field width.''
  242.              *    -- ANSI X3J11
  243.              */
  244.             flags |= ZEROPAD;
  245.             goto rflag;
  246.         case '1': case '2': case '3': case '4':
  247.         case '5': case '6': case '7': case '8': case '9':
  248.             n = 0;
  249.             do {
  250.                 n = 10 * n + todigit(*fmt);
  251.             } while (isascii(*++fmt) && isdigit(*fmt));
  252.             width = n;
  253.             --fmt;
  254.             goto rflag;
  255.         case 'L':
  256.             flags |= LONGDBL;
  257.             goto rflag;
  258.         case 'h':
  259.             flags |= SHORTINT;
  260.             goto rflag;
  261.         case 'l':
  262.             flags |= LONGINT;
  263.             goto rflag;
  264.         case 'c':
  265.             *(t = buf) = va_arg(argp, int);
  266.             size = 1;
  267.             sign = '\0';
  268.             goto pforw;
  269.         case 'D':
  270.             flags |= LONGINT;
  271.             /*FALLTHROUGH*/
  272.         case 'd':
  273.         case 'i':
  274.             ARG();
  275.             if ((long)_ulong < 0) {
  276.                 _ulong = -_ulong;
  277.                 sign = '-';
  278.             }
  279.             base = 10;
  280.             goto number;
  281. #ifdef FLOAT
  282.         case 'e':
  283.         case 'E':
  284.         case 'f':
  285.         case 'g':
  286.         case 'G':
  287.             _double = va_arg(argp, double);
  288.             /*
  289.              * don't do unrealistic precision; just pad it with
  290.              * zeroes later, so buffer size stays rational.
  291.              */
  292.             if (prec > MAXFRACT) {
  293.                 if (*fmt != 'g' && *fmt != 'G' || (flags&ALT))
  294.                     fpprec = prec - MAXFRACT;
  295.                 prec = MAXFRACT;
  296.             }
  297.             else if (prec == -1)
  298.                 prec = DEFPREC;
  299.             /*
  300.              * softsign avoids negative 0 if _double is < 0 and
  301.              * no significant digits will be shown
  302.              */
  303.             if (_double < 0) {
  304.                 softsign = '-';
  305.                 _double = -_double;
  306.             }
  307.             else
  308.                 softsign = 0;
  309.             /*
  310.              * cvt may have to round up past the "start" of the
  311.              * buffer, i.e. ``intf("%.2f", (double)9.999);'';
  312.              * if the first char isn't NULL, it did.
  313.              */
  314.             *buf = (char)NULL;
  315.             size = cvt(_double, prec, flags, &softsign, *fmt, buf,
  316.                 buf + sizeof(buf));
  317.             if (softsign)
  318.                 sign = '-';
  319.             t = *buf ? buf : buf + 1;
  320.             goto pforw;
  321. #endif /* FLOAT */
  322.         case 'n':
  323. #ifdef INTEL
  324.             if (flags & NEARPTR) {
  325.                 if (flags & LONGINT)
  326.                     *va_arg(argp, long _near *) = cnt;
  327.                 else if (flags & SHORTINT)
  328.                     *va_arg(argp, short _near *) = cnt;
  329.                 else
  330.                     *va_arg(argp, int _near *) = cnt;
  331.             } else if (flags & FARPTR) {
  332.                 if (flags & LONGINT)
  333.                     *va_arg(argp, long _far *) = cnt;
  334.                 else if (flags & SHORTINT)
  335.                     *va_arg(argp, short _far *) = cnt;
  336.                 else
  337.                     *va_arg(argp, int _far *) = cnt;
  338.             } else {
  339.                 if (flags & LONGINT)
  340.                     *va_arg(argp, long *) = cnt;
  341.                 else if (flags & SHORTINT)
  342.                     *va_arg(argp, short *) = cnt;
  343.                 else
  344.                     *va_arg(argp, int *) = cnt;
  345.             }
  346. #else
  347.             if (flags & LONGINT)
  348.                 *va_arg(argp, long *) = cnt;
  349.             else if (flags & SHORTINT)
  350.                 *va_arg(argp, short *) = cnt;
  351.             else
  352.                 *va_arg(argp, int *) = cnt;
  353. #endif
  354.             break;
  355.         case 'O':
  356.             flags |= LONGINT;
  357.             /*FALLTHROUGH*/
  358.         case 'o':
  359.             UARG();
  360.             base = 8;
  361.             goto nosign;
  362. #ifdef INTEL
  363.         case 'N':
  364.             flags |= NEARPTR;
  365.             goto rflag;
  366.         case 'F':
  367.             flags |= FARPTR;
  368.             goto rflag;
  369. #endif
  370.         case 'p':
  371.             /*
  372.              * ``The argument shall be a pointer to void.  The
  373.              * value of the pointer is converted to a sequence
  374.              * of printable characters, in an implementation-
  375.              * defined manner.''
  376.              *    -- ANSI X3J11
  377.              */
  378.             /* NOSTRICT */
  379. #ifdef INTEL
  380.             if (flags & FARPTR)
  381.                 _ulong = (u_long)va_arg(argp, void _far *);
  382.             else if (flags & NEARPTR)
  383.                 _ulong = (u_long)va_arg(argp, void _near *);
  384.             else
  385.                 _ulong = (u_long)va_arg(argp, void *);
  386. #else
  387.             _ulong = (u_long)va_arg(argp, void *);
  388. #endif
  389.             base = 16;
  390.             goto nosign;
  391.         case 's':
  392. #ifdef INTEL
  393.         {
  394.             char _far *_ft = NULL;
  395.  
  396.             if (flags & FARPTR)
  397.                 _ft = va_arg(argp, char _far *);
  398.             else if (flags & NEARPTR)
  399.                 t = va_arg(argp, char _near *);
  400.             else
  401.                 t = va_arg(argp, char *);
  402.             if (!t && !_ft)
  403.                 t = "(null)";
  404.             if (_ft) {
  405.                 if (prec >= 0) {
  406.                     char _far *p;
  407.  
  408.                     if (p = _fmemchr(_ft, 0, prec)) {
  409.                         size = p - _ft;
  410.                         if (size > prec)
  411.                             size = prec;
  412.                     } else
  413.                         size = prec;
  414.                 } else
  415.                     size = _fstrlen(_ft);
  416.                 /*
  417.                  * Far strings are truncated to BUF characters
  418.                  * in order to make them near and minimize
  419.                  * the effect on the whole doprnt.
  420.                  */
  421.                 if (size + 1 > BUF) {
  422.                     _fmemcpy(buf, _ft, BUF - 1);
  423.                     buf[BUF - 1] = '\0';
  424.                 } else
  425.                     _fmemcpy(buf, _ft, size + 1);
  426.                 t = buf;
  427.             } else {
  428.                 if (prec >= 0) {
  429.                     char *p;
  430. #ifndef FNPROTO
  431.                     char *memchr();
  432. #endif
  433.  
  434.                     if (p = memchr(t, 0, prec)) {
  435.                         size = p - t;
  436.                         if (size > prec)
  437.                             size = prec;
  438.                     } else
  439.                         size = prec;
  440.                 } else
  441.                     size = strlen(t);
  442.             }
  443.         }
  444. #else /* !INTEL */
  445.             t = va_arg(argp, char *);
  446.             if (!t)
  447.                 t = "(null)";
  448.             if (prec >= 0) {
  449.                 /*
  450.                  * can't use strlen; can only look for the
  451.                  * NUL in the first `prec' characters, and
  452.                  * strlen() will go further.
  453.                  */
  454.                 char *p;
  455. #ifndef FNPROTO
  456.                 char *memchr();
  457. #endif
  458.  
  459.                 if (p = memchr(t, 0, prec)) {
  460.                     size = p - t;
  461.                     if (size > prec)
  462.                         size = prec;
  463.                 } else
  464.                     size = prec;
  465.             } else
  466.                 size = strlen(t);
  467. #endif
  468.             sign = '\0';
  469.             goto pforw;
  470.         case 'U':
  471.             flags |= LONGINT;
  472.             /*FALLTHROUGH*/
  473.         case 'u':
  474.             UARG();
  475.             base = 10;
  476.             goto nosign;
  477.         case 'X':
  478.             digs = "0123456789ABCDEF";
  479.             /* FALLTHROUGH */
  480.         case 'x':
  481.             UARG();
  482.             base = 16;
  483.             /* leading 0x/X only if non-zero */
  484.             if (flags & ALT && _ulong != 0)
  485.                 flags |= HEXPREFIX;
  486.  
  487.             /* unsigned conversions */
  488. nosign:            sign = '\0';
  489.             /*
  490.              * ``... diouXx conversions ... if a precision is
  491.              * specified, the 0 flag will be ignored.''
  492.              *    -- ANSI X3J11
  493.              */
  494. number:            if ((dprec = prec) >= 0)
  495.                 flags &= ~ZEROPAD;
  496.  
  497.             /*
  498.              * ``The result of converting a zero value with an
  499.              * explicit precision of zero is no characters.''
  500.              *    -- ANSI X3J11
  501.              */
  502.             t = buf + BUF;
  503.             if (_ulong != 0 || prec != 0) {
  504.                 do {
  505.                     *--t = digs[_ulong % base];
  506.                     _ulong /= base;
  507.                 } while (_ulong);
  508.                 digs = "0123456789abcdef";
  509.                 if (flags & ALT && base == 8 && *t != '0')
  510.                     *--t = '0'; /* octal leading 0 */
  511.             }
  512.             size = buf + BUF - t;
  513.  
  514. pforw:
  515.             /*
  516.              * All reasonable formats wind up here.  At this point,
  517.              * `t' points to a string which (if not flags&LADJUST)
  518.              * should be padded out to `width' places.  If
  519.              * flags&ZEROPAD, it should first be prefixed by any
  520.              * sign or other prefix; otherwise, it should be blank
  521.              * padded before the prefix is emitted.  After any
  522.              * left-hand padding and prefixing, emit zeroes
  523.              * required by a decimal [diouxX] precision, then print
  524.              * the string proper, then emit zeroes required by any
  525.              * leftover floating precision; finally, if LADJUST,
  526.              * pad with blanks.
  527.              */
  528.  
  529.             /*
  530.              * compute actual size, so we know how much to pad
  531.              * fieldsz excludes decimal prec; realsz includes it
  532.              */
  533.             fieldsz = size + fpprec;
  534.             if (sign)
  535.                 fieldsz++;
  536.             if (flags & HEXPREFIX)
  537.                 fieldsz += 2;
  538.             realsz = dprec > fieldsz ? dprec : fieldsz;
  539.  
  540.             /* right-adjusting blank padding */
  541.             if ((flags & (LADJUST|ZEROPAD)) == 0 && width)
  542.                 for (n = realsz; n < width; n++)
  543.                     PUTC(' ');
  544.             /* prefix */
  545.             if (sign)
  546.                 PUTC(sign);
  547.             if (flags & HEXPREFIX) {
  548.                 PUTC('0');
  549.                 PUTC((char)*fmt);
  550.             }
  551.             /* right-adjusting zero padding */
  552.             if ((flags & (LADJUST|ZEROPAD)) == ZEROPAD)
  553.                 for (n = realsz; n < width; n++)
  554.                     PUTC('0');
  555.             /* leading zeroes from decimal precision */
  556.             for (n = fieldsz; n < dprec; n++)
  557.                 PUTC('0');
  558.  
  559.             /* the string or number proper */
  560.             if (fp->_cnt - (n = size) >= 0 &&
  561.                 (fp->_flag & _IOLBF) == 0) {
  562.                 fp->_cnt -= n;
  563.                 memcpy((char *)fp->_ptr, t, n);
  564.                 fp->_ptr += n;
  565.             } else
  566.                 while (--n >= 0)
  567.                     PUTC(*t++);
  568.             /* trailing f.p. zeroes */
  569.             while (--fpprec >= 0)
  570.                 PUTC('0');
  571.             /* left-adjusting padding (always blank) */
  572.             if (flags & LADJUST)
  573.                 for (n = realsz; n < width; n++)
  574.                     PUTC(' ');
  575.             /* finally, adjust cnt */
  576.             cnt += width > realsz ? width : realsz;
  577.             break;
  578.         case '\0':    /* "%?" prints ?, unless ? is NULL */
  579.             return (cnt);
  580.         default:
  581.             PUTC((char)*fmt);
  582.             cnt++;
  583.         }
  584.     }
  585.     /* NOTREACHED */
  586. }
  587.  
  588. #ifdef FLOAT
  589. static
  590. cvt(number, prec, flags, signp, fmtch, startp, endp)
  591.     double number;
  592.     register int prec;
  593.     int flags;
  594.     u_char fmtch;
  595.     char *signp, *startp, *endp;
  596. {
  597.     register char *p, *t;
  598.     register double fract;
  599.     int dotrim, expcnt, gformat;
  600.     double integer, tmp, modf();
  601.     char *exponent(), *round();
  602.  
  603.     dotrim = expcnt = gformat = 0;
  604.     fract = modf(number, &integer);
  605.  
  606.     /* get an extra slot for rounding. */
  607.     t = ++startp;
  608.  
  609.     /*
  610.      * get integer portion of number; put into the end of the buffer; the
  611.      * .01 is added for modf(356.0 / 10, &integer) returning .59999999...
  612.      */
  613.     for (p = endp - 1; integer; ++expcnt) {
  614.         tmp = modf(integer / 10, &integer);
  615.         *p-- = tochar((int)((tmp + .01) * 10));
  616.     }
  617.     switch(fmtch) {
  618.     case 'f':
  619.         /* reverse integer into beginning of buffer */
  620.         if (expcnt)
  621.             for (; ++p < endp; *t++ = *p);
  622.         else
  623.             *t++ = '0';
  624.         /*
  625.          * if precision required or alternate flag set, add in a
  626.          * decimal point.
  627.          */
  628.         if (prec || flags&ALT)
  629.             *t++ = '.';
  630.         /* if requires more precision and some fraction left */
  631.         if (fract) {
  632.             if (prec)
  633.                 do {
  634.                     fract = modf(fract * 10, &tmp);
  635.                     *t++ = tochar((int)tmp);
  636.                 } while (--prec && fract);
  637.             if (fract)
  638.                 startp = round(fract, (int *)NULL, startp,
  639.                     t - 1, (char)0, signp);
  640.         }
  641.         for (; prec--; *t++ = '0');
  642.         break;
  643.     case 'e':
  644.     case 'E':
  645. eformat:    if (expcnt) {
  646.             *t++ = *++p;
  647.             if (prec || flags&ALT)
  648.                 *t++ = '.';
  649.             /* if requires more precision and some integer left */
  650.             for (; prec && ++p < endp; --prec)
  651.                 *t++ = *p;
  652.             /*
  653.              * if done precision and more of the integer component,
  654.              * round using it; adjust fract so we don't re-round
  655.              * later.
  656.              */
  657.             if (!prec && ++p < endp) {
  658.                 fract = 0;
  659.                 startp = round((double)0, &expcnt, startp,
  660.                     t - 1, *p, signp);
  661.             }
  662.             /* adjust expcnt for digit in front of decimal */
  663.             --expcnt;
  664.         }
  665.         /* until first fractional digit, decrement exponent */
  666.         else if (fract) {
  667.             /* adjust expcnt for digit in front of decimal */
  668.             for (expcnt = -1;; --expcnt) {
  669.                 fract = modf(fract * 10, &tmp);
  670.                 if (tmp)
  671.                     break;
  672.             }
  673.             *t++ = tochar((int)tmp);
  674.             if (prec || flags&ALT)
  675.                 *t++ = '.';
  676.         }
  677.         else {
  678.             *t++ = '0';
  679.             if (prec || flags&ALT)
  680.                 *t++ = '.';
  681.         }
  682.         /* if requires more precision and some fraction left */
  683.         if (fract) {
  684.             if (prec)
  685.                 do {
  686.                     fract = modf(fract * 10, &tmp);
  687.                     *t++ = tochar((int)tmp);
  688.                 } while (--prec && fract);
  689.             if (fract)
  690.                 startp = round(fract, &expcnt, startp,
  691.                     t - 1, (char)0, signp);
  692.         }
  693.         /* if requires more precision */
  694.         for (; prec--; *t++ = '0');
  695.  
  696.         /* unless alternate flag, trim any g/G format trailing 0's */
  697.         if (gformat && !(flags&ALT)) {
  698.             while (t > startp && *--t == '0');
  699.             if (*t == '.')
  700.                 --t;
  701.             ++t;
  702.         }
  703.         t = exponent(t, expcnt, fmtch);
  704.         break;
  705.     case 'g':
  706.     case 'G':
  707.         /* a precision of 0 is treated as a precision of 1. */
  708.         if (!prec)
  709.             ++prec;
  710.         /*
  711.          * ``The style used depends on the value converted; style e
  712.          * will be used only if the exponent resulting from the
  713.          * conversion is less than -4 or greater than the precision.''
  714.          *    -- ANSI X3J11
  715.          */
  716.         if (expcnt > prec || !expcnt && fract && fract < .0001) {
  717.             /*
  718.              * g/G format counts "significant digits, not digits of
  719.              * precision; for the e/E format, this just causes an
  720.              * off-by-one problem, i.e. g/G considers the digit
  721.              * before the decimal point significant and e/E doesn't
  722.              * count it as precision.
  723.              */
  724.             --prec;
  725.             fmtch -= 2;        /* G->E, g->e */
  726.             gformat = 1;
  727.             goto eformat;
  728.         }
  729.         /*
  730.          * reverse integer into beginning of buffer,
  731.          * note, decrement precision
  732.          */
  733.         if (expcnt)
  734.             for (; ++p < endp; *t++ = *p, --prec);
  735.         else
  736.             *t++ = '0';
  737.         /*
  738.          * if precision required or alternate flag set, add in a
  739.          * decimal point.  If no digits yet, add in leading 0.
  740.          */
  741.         if (prec || flags&ALT) {
  742.             dotrim = 1;
  743.             *t++ = '.';
  744.         }
  745.         else
  746.             dotrim = 0;
  747.         /* if requires more precision and some fraction left */
  748.         if (fract) {
  749.             if (prec) {
  750.                 do {
  751.                     fract = modf(fract * 10, &tmp);
  752.                     *t++ = tochar((int)tmp);
  753.                 } while(!tmp);
  754.                 while (--prec && fract) {
  755.                     fract = modf(fract * 10, &tmp);
  756.                     *t++ = tochar((int)tmp);
  757.                 }
  758.             }
  759.             if (fract)
  760.                 startp = round(fract, (int *)NULL, startp,
  761.                     t - 1, (char)0, signp);
  762.         }
  763.         /* alternate format, adds 0's for precision, else trim 0's */
  764.         if (flags&ALT)
  765.             for (; prec--; *t++ = '0');
  766.         else if (dotrim) {
  767.             while (t > startp && *--t == '0');
  768.             if (*t != '.')
  769.                 ++t;
  770.         }
  771.     }
  772.     return(t - startp);
  773. }
  774.  
  775. static char *
  776. round(fract, exp, start, end, ch, signp)
  777.     double fract;
  778.     int *exp;
  779.     register char *start, *end;
  780.     char ch, *signp;
  781. {
  782.     double tmp;
  783.  
  784.     if (fract)
  785.         (void)modf(fract * 10, &tmp);
  786.     else
  787.         tmp = todigit(ch);
  788.     if (tmp > 4)
  789.         for (;; --end) {
  790.             if (*end == '.')
  791.                 --end;
  792.             if (++*end <= '9')
  793.                 break;
  794.             *end = '0';
  795.             if (end == start) {
  796.                 if (exp) {    /* e/E; increment exponent */
  797.                     *end = '1';
  798.                     ++*exp;
  799.                 }
  800.                 else {        /* f; add extra digit */
  801.                     *--end = '1';
  802.                     --start;
  803.                 }
  804.                 break;
  805.             }
  806.         }
  807.     /* ``"%.3f", (double)-0.0004'' gives you a negative 0. */
  808.     else if (*signp == '-')
  809.         for (;; --end) {
  810.             if (*end == '.')
  811.                 --end;
  812.             if (*end != '0')
  813.                 break;
  814.             if (end == start)
  815.                 *signp = 0;
  816.         }
  817.     return(start);
  818. }
  819.  
  820. static char *
  821. exponent(p, exp, fmtch)
  822.     register char *p;
  823.     register int exp;
  824.     u_char fmtch;
  825. {
  826.     register char *t;
  827.     char expbuf[MAXEXP];
  828.  
  829.     *p++ = fmtch;
  830.     if (exp < 0) {
  831.         exp = -exp;
  832.         *p++ = '-';
  833.     }
  834.     else
  835.         *p++ = '+';
  836.     t = expbuf + MAXEXP;
  837.     if (exp > 9) {
  838.         do {
  839.             *--t = tochar(exp % 10);
  840.         } while ((exp /= 10) > 9);
  841.         *--t = tochar(exp);
  842.         for (; t < expbuf + MAXEXP; *p++ = *t++);
  843.     }
  844.     else {
  845.         *p++ = '0';
  846.         *p++ = tochar(exp);
  847.     }
  848.     return(p);
  849. }
  850. #endif /* FLOAT */
  851.